{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Part 3: Advanced Remote Execution Tools\n",
    "\n",
    "In the last section we train a toy model using Federated Learning. We do am by calling .send() and .get() on our model, we send am to the location of trainig data, updating it, and we come bring am back. At the end of the example We discover sey we fit go further on how to protect people privacy. The way we go do am, we go average the gradients **before** calling .get(). If we do am like this, we no go see people exact gradient (this one dey good for protecting privacy!!!)\n",
    "\n",
    "\n",
    "So that we go fit do am, we go need some pieces:\n",
    "- use a pointer to send a Tensor directly to another worker\n",
    "\n",
    "Another tin wey you go learn na advanced tensor operations and which one go helep us with the example and some in the future!\n",
    "\n",
    "Person wey write am:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",
    "Person wey translate am:\n",
    "- Temitọpẹ Ọladokun - Twitter: [@techie991](https://twitter.com/techie991)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Section 3.1 - Pointers to Pointers\n",
    "\n",
    "As you know, PointerTensor objects just dey like normal tensors. In fact, they are _so much like tensors_ wey we fit have pointers **to** the pointers. Check am out!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob = sy.VirtualWorker(hook, id='bob')\n",
    "alice = sy.VirtualWorker(hook, id='alice')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# this one na local tensor\n",
    "x = torch.tensor([1,2,3,4])\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# this one go send local tensor to Bob\n",
    "x_ptr = x.send(bob)\n",
    "\n",
    "# this one na the pointer\n",
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# we go send the pointer to alice!!!\n",
    "pointer_to_x_ptr = x_ptr.send(alice)\n",
    "\n",
    "pointer_to_x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Wetin happen?\n",
    "\n",
    "For the previous example, we create tensor called `x` and we send am to Bob, we come create pointer on top our local machine (`x_ptr`).\n",
    "\n",
    "We go call `x_ptr.send(alice)` wey go **sent the pointer** to Alice. \n",
    "\n",
    "Look am wella, Data no change level!  Instead, pointer move to data!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# As you dey see the above, Bob still get the actual data (data dey always dey stored in a LocalTensor type). \n",
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Alice, on the other hand, get x_ptr!! (notice sey he dey point at Bob)\n",
    "alice._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# we fit use .get() to get x_ptr back from Alice\n",
    "\n",
    "x_ptr = pointer_to_x_ptr.get()\n",
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# we fit use x_ptr to get x back from Bob!\n",
    "\n",
    "x = x_ptr.get()\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Arithmetic on Pointer -> Pointer -> Data Object\n",
    "\n",
    "Just like with normal pointers, we fit perform arbitrary Pytorch operations across these tensors"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p2p2x = torch.tensor([1,2,3,4,5]).send(bob).send(alice)\n",
    "\n",
    "y = p2p2x + p2p2x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y.get().get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p2p2x.get().get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Section 3.2 - Pointer Chain Operations\n",
    "\n",
    "In the last section whenever we call a .send() or a .get() operation, we call that operation directly on the tensor on our local machine. If you get a chain of pointers, sometimes we wan dey able to call operations like .get() or .send() on the **last** pointer in the chain (such as sending data directly from one worker to another). To accomplish this, we go use function wey dey specially design for this privacy preserving operation.\n",
    "\n",
    "These operations are:\n",
    "\n",
    "- `my_pointer2pointer.move(another_worker)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# xis now a pointer to the data wey dey on top Bob's machine.\n",
    "x = torch.tensor([1,2,3,4,5]).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('  bob:', bob._objects)\n",
    "print('alice:',alice._objects)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = x.move(alice)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('  bob:', bob._objects)\n",
    "print('alice:',alice._objects)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Excellent! Now we are equiped with the tools wey we fit use to perfom remote **gradient averaging** using a trusted aggregator!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Congratulations!!! - Oya Join the Community!\n",
    "\n",
    "Clap for una sef as you don finish this notebook tutorial! If you enjoy am and you wan join the movement towards privacy preserving, decentralized ownership of AI and the AI supply chain (data), follow the steps wey dey below.\n",
    "\n",
    "### Star PySyft on GitHub\n",
    "\n",
    "The easiset way to helep our community na to star the GitHub repos! This go helep raise awareness of the tools we dey build.\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Join our Slack!\n",
    "\n",
    "To follow up bumper to bumper on how latest advancements, join our community! You can do so by filling out the form at [http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### Join a Code Project!\n",
    "\n",
    "The best way to contribute to our community na to become code contributor! You fit go to PySyft GitHub Issues page and filter for \"Projects\". E go show you all the top level Tickets giving an overview of what projects you fit join! If you no wan join any project, but you wan code small, you fit look for more \"one off\" mini-projects by searching for GitHub issues marked \"good first issue\"\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Donate\n",
    "\n",
    "If you no get time to contribute to our codebase, but still like to lend support, you fit be a Backer on our Open Collective. All donations wey we get na for our web hosting and other community expenses such as hackathons and meetups! meetups!\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
